#include <omnetpp.h>
#include <crng.h>
#include "LT3LCG.h"

Register_PerRunConfigOption(CFGID_BUFFERSIZE_SELECCION, "buffer-%-lt3seleccion", CFG_INT, "100", "When LT3Seleccion is selected as random number generator: buffer size (100 as default) for RNG number k. (Substitute k for '%' in the key.)");

using std::cout;

class LT3Selection : public cRNG {

private:
	LT3LCG *lcg1;
	LT3LCG *lcg2;
	unsigned long *buffer;
	unsigned long bufferSize;
	
public:
	LT3Selection();
	~LT3Selection();
	virtual void initialize(int seedSet, int rngId, int numRngs, int parsimProcId, int parsimNumPartitions, cConfiguration *cfg);
	virtual unsigned long numbersDrawn();
	virtual void selfTest();
	virtual unsigned long intRand();
	virtual double doubleRand();
	virtual unsigned long intRand(unsigned long n);
	virtual unsigned long intRandMax();
	virtual double doubleRandNonz();
	virtual double doubleRandIncl1();
	virtual double doubleRandNonzIncl1();
};

// The class needs to be registered with OMNeT
Register_Class(LT3Selection);
//----------------------------------------------------------------------------

LT3Selection::~LT3Selection() {
	if (lcg1)
		delete lcg1;
	if (lcg2)
		delete lcg2;
}

LT3Selection::LT3Selection() {
	lcg1 = NULL;
	lcg2 = NULL;
}

void LT3Selection::initialize(int seedSet, int rngId, int numRngs, int parsimProcId, int parsimNumPartitions, cConfiguration * cfg) {

	lcg1 = new LT3LCG();
	lcg2 = new LT3LCG();

	// Update rngId properly to initialize the LT3LCG generators
	lcg1->initialize(seedSet, rngId*10, numRngs, parsimProcId, parsimNumPartitions, cfg);
	lcg2->initialize(seedSet, rngId*10+1, numRngs, parsimProcId, parsimNumPartitions, cfg);

    char key[32];
    sprintf(key,  "buffer-%d-lt3seleccion", rngId);
    const char *value = cfg->getConfigValue(key);
    if (value==NULL) bufferSize=100;
    else bufferSize = cConfiguration::parseLong(value,"100.");
	buffer = new unsigned long[bufferSize];

	// Init the buffer values
	for (unsigned long i = 0; i < bufferSize; i++) {
		buffer[i] = lcg1->intRand();
	}

	numDrawn=0;
	cout << "Selection random generator loaded with:" << endl;
	if (lcg1) cout << "\tLCG "<< rngId*10 << ", samples to return "<< endl;
	if (lcg2) cout << "\tLCG "<< rngId*10+1 << ", to randomize the LCG "<< rngId*10 << endl;
	cout << "\tBuffer size: " << bufferSize << endl;
}

unsigned long LT3Selection::numbersDrawn() {
	return 	numDrawn;
}

void LT3Selection::selfTest() {
	cout << "Initializing the Selection Random Generator" << endl;
}

unsigned long LT3Selection::intRand() {

	unsigned long value;
	unsigned long index;
	numDrawn++;

	// Find the buffer index
	index = lcg2->intRand(bufferSize);

	// Store the value to return
	value = buffer[index];

	// Next sample
	buffer[index] = lcg1->intRand();

	return value;
}

double LT3Selection::doubleRand() {

	double value = (double) intRand();
	return value /((double) lcg1->intRandMax()+1);
}

unsigned long LT3Selection::intRand(unsigned long n) {
	return (unsigned long) ( doubleRand() * ((double) n));
}

unsigned long LT3Selection::intRandMax() {
	return lcg1->intRandMax();
}

double LT3Selection::doubleRandNonz() {

	double num;
	while ((num = doubleRand()) == 0);
	return num;
}

double LT3Selection::doubleRandIncl1() {
	return doubleRand();
}

double LT3Selection::doubleRandNonzIncl1() {
	return doubleRand();
}
